package server

import (
	"context"
	"os"
	"os/signal"
	"syscall"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v7"
	"github.com/smallnest/rpcx/server"
	"gorm.io/gorm"

	"octopus/hextech/config"
	"octopus/hextech/nlog"
	"octopus/hextech/server/shutdown"
	"octopus/hextech/server/shutdown/shutdownmanagers/posixsignal"
)

var APP *Server

type Server struct {
	gs      *shutdown.GracefulShutdown
	options *config.Options
	ctx     context.Context
	name    string
}

func New(configName, configPath string) *Server {
	ctx := context.Background()
	gs := shutdown.New()
	gs.AddShutdownManager(posixsignal.NewPosixSignalManager())

	// 加载静态配置
	opts, err := config.NewOptions(configName, configPath)
	if err != nil {
		nlog.Fatalf(ctx, "unable to initialize command options: %v", err)
	}

	// 资源注册
	err = opts.Complete()
	if err != nil {
		nlog.Fatalf(ctx, "unable to complete options: %v", err)
	}
	APP = &Server{
		ctx:     ctx,
		gs:      gs,
		options: opts,
	}
	return APP
}

func (d *Server) Run() {
	if d == nil {
		return
	}
	stopCh := make(chan os.Signal)
	signal.Notify(stopCh, syscall.SIGTERM, syscall.SIGINT)

	defer nlog.CloseLogger()

	d.runServer()
	<-stopCh
}

func (d *Server) runServer() {
	// 添加退出回调
	d.gs.AddShutdownCallback(shutdown.ShutdownFunc(func(string) error {
		d.options.Close()
		return nil
	}))

	// 启动退出管理
	if err := d.gs.Start(); err != nil {
		nlog.Fatalf(d.ctx, "start shutdown manager failed: %s", err.Error())
	}

	// 启动服务
	d.options.RunServer()
}

// SetHttpRouter 启动Http服务，则必须传递web路由函数
func (d *Server) SetHttpRouter(router func(engine *gin.Engine)) *Server {
	if d.options.WebServer != nil {
		d.options.WebServer.RouterFunc = router
	}
	return d
}

// SetRpcXReceiver 启动RPC服务，则必须要传递路由结构体的指针
// Receiver: RpcX路由结构体的指针
// plugins: 路由对应的过滤插件，可以没有
func (d *Server) SetRpcXReceiver(receiver interface{}, plugins []*server.Plugin) *Server {
	if d.options.RpcXServer != nil {
		d.options.RpcXServer.Receiver = receiver
		d.options.RpcXServer.Plugins = plugins
	}
	return d
}

func (d *Server) GetDB() *gorm.DB {
	return d.options.Db
}

func (d *Server) GetRedis() *redis.Client {
	return d.options.Redis
}
